package com.nulldev.util.web.nTube.util;

import static com.nulldev.util.web.nTube.pages.Urlizer.urlizer;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.VariableAPI.util.strings.splitters.Splitters;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.concurrency.threadIt.v4.emapi.extensions.IFutureTaskFlags;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.concurrency.threadIt.v4.utils.impls.TaskUtils;
import com.nulldev.util.data.Variables;
import com.nulldev.util.data.variable;
import com.nulldev.util.data.Arrays.arrays.FastLists;
import com.nulldev.util.data.legacy.XML.lXML;
import com.nulldev.util.data.legacy.XML.lXML.Attr;
import com.nulldev.util.data.legacy.XML.lXML.Doc;
import com.nulldev.util.data.legacy.XML.lXML.E;
import com.nulldev.util.data.legacy.XML.lXML.Node;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.web.HttpServer3.pages.v3.HttpPage3.Request;
import com.nulldev.util.web.YouTubeExtractor.formats.FileType;
import com.nulldev.util.web.YouTubeExtractor.formats.Format;
import com.nulldev.util.web.YouTubeExtractor.metadata.YouTubeMetadata;
import com.nulldev.util.web.YouTubeExtractor.video.YouTubeVideo;

public class DashMPDGenerator2 {

	private static final Logger log = LoggerFactory.getLoggerDT(Arguments.hasArgument("--debug", "--trace", "--DashMPDGenerator.debug"));
	/* */
	private static final String MAX_PLAYOUT_RATE_GLOBAL = String.valueOf(4);
	private static final boolean ENABLE_NO_CODING_DEPENDENCY = Arguments.hasArgument("--DASHMPD2.noCodingDependency");
	private static final boolean ENABLE_ADAPTATION_SET_DIMENSIONS = Arguments.notArguments("--DASHMPD2.disableASDims");
	private static final boolean ENABLE_ADAPTATION_SET_SWITCHING = Arguments.notArguments("--DASHMPD2.noAdaptationSetSwitching");
	private static final boolean ENABLE_SEGMENT_ALIGNMENT = Arguments.hasArgument("--DASHMPD2.segmentAlignment");
	private static final boolean ENABLE_SUB_SEGMENT_ALIGNMENT = Arguments.hasArgument("--DASHMPD2.subSegmentAlignment");
	/* */
	private static final int LOW_RESOLUTION_THRESHOLD = 480;
	/* Use 2048x1152 as our 1080p reference (blame AMD) */
	private static final int MODERATE_RESOLUTION_THRESHOLD = 1152;
	/* */
	private static final int DATA_SAVER_AV1_LIMIT = 720;
	private static final int DATA_SAVER_VP9_LIMIT = 480;
	private static final int DATA_SAVER_MP4_LIMIT = 360;

	public static String make(final List<YouTubeVideo> videos, final Request r, final YouTubeMetadata metadata, final boolean disableAV1codecs,
			final boolean djsRemove30FPSVideos, final boolean djsRemove60FPSVideos, final variable<Boolean> has60FPS, final boolean allowWebM,
			final boolean reduceLoadForAll, final boolean audioOnly, final boolean enableCDNRandomization, final boolean dataSaver) {
		return make(videos, r, metadata, disableAV1codecs, djsRemove30FPSVideos, djsRemove60FPSVideos, has60FPS, allowWebM, reduceLoadForAll, false, audioOnly,
				enableCDNRandomization, dataSaver);
	}

	public static String make(final List<YouTubeVideo> videos, final Request r, final YouTubeMetadata metadata, final boolean disableAV1codecs,
			final boolean djsRemove30FPSVideos, final boolean djsRemove60FPSVideos, final variable<Boolean> has60FPS, final boolean allowWebM,
			final boolean reduceLoadForAll, final boolean av1OnlyLowRes, final boolean audioOnly, final boolean enableCDNRandomization,
			final boolean dataSaver) {
		Variables.requireNonNull(videos, "videos");
		Variables.requireNonNull(r, "r");
		Variables.requireNonNull(metadata, "metadata");
		Variables.requireNonNull(has60FPS, "has60FPS");

		if (videos.isEmpty()) {
			throw new UnsupportedOperationException("No videos found!");
		}

		if (metadata.isLiveStream()) {
			log.dwarn("Skipping MPD generation, video is a livestream!");
			return null;
		}

		final String _intUrl;
		final String _hdr = r.header("Host", "");
		if (!StringsUtil.isZeroLengthOrNull(_hdr)) {
			if (NTubeUtils.wasOriginHTTPS(r)) {
				_intUrl = "https://" + _hdr;
			} else {
				_intUrl = "http://" + _hdr;
			}
		} else {
			_intUrl = _hdr;
		}

		final List<YouTubeVideo> mp4AV1 = FastLists.list();
		final List<YouTubeVideo> webmVideo = FastLists.list();
		final List<YouTubeVideo> webmAV1 = FastLists.list();
		final List<YouTubeVideo> mp4video = FastLists.list();

		final List<YouTubeVideo> mpeg4audio = FastLists.list();
		final List<YouTubeVideo> webmAudio = FastLists.list();

		final AtomicBoolean hasAudioLangs = new AtomicBoolean();
		videos.parallelStream().forEach((video) -> {
			if (video == null) {
				log.warn("Got a null YouTubeVideo source. [video=" + video + "]");
				return;
			}
			final Format f = video.getFormat();
			if (f == null)
				return;
			if (reduceLoadForAll && f.getHeight() > MODERATE_RESOLUTION_THRESHOLD)
				return;
			if (f.hasVideo() && !f.hasAudio()) {
				if (audioOnly)
					return;
				switch (f.getVideoCodec()) {
					case AV1: {
						if (disableAV1codecs)
							break;
						if (av1OnlyLowRes && !dataSaver && f.getHeight() > LOW_RESOLUTION_THRESHOLD)
							break;
						if (dataSaver && f.getHeight() > DATA_SAVER_AV1_LIMIT)
							break;
						if (f.getFileType() == FileType.MP4) {
							mp4AV1.add(video);
						} else {
							webmAV1.add(video);
						}
						break;
					}
					case VP9:
						if (dataSaver && f.getHeight() > DATA_SAVER_VP9_LIMIT)
							break;
						webmVideo.add(video);
						break;
					case H264:
						if (dataSaver && f.getHeight() > DATA_SAVER_MP4_LIMIT)
							break;
						mp4video.add(video);
						break;
					default:
						log.warn("Unknown video codec: " + f.getVideoCodec());
						break;
				}
			} else if (f.hasAudio() && !f.hasVideo()) {
				if ((f.hasTrackID() || f.hasTrackLanguage()) && !hasAudioLangs.get())
					hasAudioLangs.lazySet(true);
				switch (f.getAudioCodec()) {
					case AAC:
						mpeg4audio.add(video);
						break;
					case OPUS:
						webmAudio.add(video);
						break;
					default:
						log.warn("Unknown audio codec: " + f.getAudioCodec());
						break;
				}
			} else {
				return;
			}
		});
		log.debug("[DJS] generate::video_init");
		final Doc d = lXML.doc();
		final E mpd = d.add(lXML.e("MPD"));
		mpd.add(new Attr("xmlns", "urn:mpeg:dash:schema:mpd:2011"));
		mpd.add(new Attr("profiles", "urn:mpeg:dash:profile:full:2011"));
		mpd.add(new Attr("minBufferTime", "PT1.5S"));
		mpd.add(new Attr("type", "static"));
		mpd.add(new Attr("mediaPresentationDuration", "PT" + (metadata.getDuration()) + "S"));
		log.debug("[DJS] generate::base");
		final E period = mpd.add(lXML.e("Period"));
		final AtomicInteger value = new AtomicInteger();
		final Executable<Void> mp4_audio = ExecutorManager.globalAsyncSubmit(() -> {
			// MPEG 4 audio
			if (!mpeg4audio.isEmpty()) {
				if (hasAudioLangs.get()) {
					final Map<String, List<YouTubeVideo>> trackedVids = ArrayUtils.splitListByFunctionWithHash(mpeg4audio, (e) -> {
						if (e == null || e.getFormat() == null)
							return "null";
						return e.getFormat().getTrackLanguage();
					});
					for (final Map.Entry<String, List<YouTubeVideo>> langs : trackedVids.entrySet()) {
						if (langs == null || langs.getKey() == null || langs.getValue() == null)
							continue;
						final int id = value.getAndIncrement();
						final E adaptationSet = period.add(lXML.e("AdaptationSet"));
						adaptationSet.add(new Attr("id", String.valueOf(id)));
						adaptationSet.add(new Attr("mimeType", "audio/mp4"));
						if (ENABLE_SEGMENT_ALIGNMENT) {
							adaptationSet.add(new Attr("startWithSAP", "1"));
							adaptationSet.add(new Attr("segmentAlignment", "true"));
						} else if (ENABLE_SUB_SEGMENT_ALIGNMENT) {
							adaptationSet.add(new Attr("subsegmentStartsWithSAP", "1"));
							adaptationSet.add(new Attr("subsegmentAlignment", "true"));
						}
						adaptationSet.add(new Attr("lang", langs.getKey()));
						adaptationSet.add(new Attr("contentType", "audio"));
						for (final YouTubeVideo mp4a : langs.getValue()) {
							if (mp4a == null)
								continue;
							final E representation = adaptationSet.add(lXML.e("Representation"));
							representation.add(new Attr("id", String.valueOf(mp4a.getTag())));
							if (mp4a.getFormat().hasTrackLanguage())
								representation.add(new Attr("lang", mp4a.getFormat().getTrackID()));
							representation.add(new Attr("codecs", StringsUtil.substringBetweenO(mp4a.getCodecMime(), "\"", "\"")));
							representation.add(new Attr("bandwidth", String.valueOf(mp4a.getBitrate())));
							/* 1.5.3+ (Project Accessible) */
							if (mp4a.getFormat().hasTrackLanguage()) {
								final E label = representation.add(lXML.e("Label"));
								label.setText(mp4a.getFormat().getTrackLanguage());
							}
							final E role = representation.add(lXML.e("Role"));
							role.add(new Attr("schemeIdUri", "urn:mpeg:dash:role:2011"));
							role.add(new Attr("value", (mp4a.getFormat().isDefaultTrack() ? "main" : "alternate")));
							// init (0-xxx), index (xxx-xxxx)
							final E audioChannelConfiguration = representation.add(lXML.e("AudioChannelConfiguration"));
							audioChannelConfiguration.add(new Attr("schemeIdUri", "urn:mpeg:dash:23003:3:audio_channel_configuration:2011"));
							audioChannelConfiguration.add(new Attr("value", String.valueOf(mp4a.getFormat().getChannels())));
							representation.add(lXML.e("BaseURL", _intUrl + urlizer(mp4a.videoURL(enableCDNRandomization))));
							final E segmentBase = representation.add(lXML.e("SegmentBase"));
							segmentBase.add(new Attr("indexRange", mp4a.getIndexPayload()));
							final E initialization = segmentBase.add(lXML.e("Initialization"));
							initialization.add(new Attr("range", mp4a.getInitPayload()));
						}
					}
				} else {
					final int id = value.getAndIncrement();
					final E adaptationSet = period.add(lXML.e("AdaptationSet"));
					adaptationSet.add(new Attr("id", String.valueOf(id)));
					adaptationSet.add(new Attr("mimeType", "audio/mp4"));
					if (ENABLE_SEGMENT_ALIGNMENT) {
						adaptationSet.add(new Attr("startWithSAP", "1"));
						adaptationSet.add(new Attr("segmentAlignment", "true"));
					} else if (ENABLE_SUB_SEGMENT_ALIGNMENT) {
						adaptationSet.add(new Attr("subsegmentStartsWithSAP", "1"));
						adaptationSet.add(new Attr("subsegmentAlignment", "true"));
					}
					adaptationSet.add(new Attr("contentType", "audio"));
					for (final YouTubeVideo mp4a : mpeg4audio) {
						if (mp4a == null)
							continue;
						final E representation = adaptationSet.add(lXML.e("Representation"));
						representation.add(new Attr("id", String.valueOf(mp4a.getTag())));
						if (mp4a.getFormat().hasTrackLanguage())
							representation.add(new Attr("lang", mp4a.getFormat().getTrackID()));
						representation.add(new Attr("codecs", StringsUtil.substringBetweenO(mp4a.getCodecMime(), "\"", "\"")));
						representation.add(new Attr("bandwidth", String.valueOf(mp4a.getBitrate())));
						/* 1.5.3+ (Project Accessible) */
						if (mp4a.getFormat().hasTrackLanguage()) {
							final E label = representation.add(lXML.e("Label"));
							label.setText(mp4a.getFormat().getTrackLanguage());
						}
						final E role = representation.add(lXML.e("Role"));
						role.add(new Attr("schemeIdUri", "urn:mpeg:dash:role:2011"));
						role.add(new Attr("value", (mp4a.getFormat().isDefaultTrack() ? "main" : "alternate")));
						// init (0-xxx), index (xxx-xxxx)
						final E audioChannelConfiguration = representation.add(lXML.e("AudioChannelConfiguration"));
						audioChannelConfiguration.add(new Attr("schemeIdUri", "urn:mpeg:dash:23003:3:audio_channel_configuration:2011"));
						audioChannelConfiguration.add(new Attr("value", String.valueOf(mp4a.getFormat().getChannels())));
						representation.add(lXML.e("BaseURL", _intUrl + urlizer(mp4a.videoURL(enableCDNRandomization))));
						final E segmentBase = representation.add(lXML.e("SegmentBase"));
						segmentBase.add(new Attr("indexRange", mp4a.getIndexPayload()));
						final E initialization = segmentBase.add(lXML.e("Initialization"));
						initialization.add(new Attr("range", mp4a.getInitPayload()));
					}
				}
			}
			log.debug("[DJS] generate::mp4_audio");
			return null;
		}, IFutureTaskFlags.FT_HAS_SYNCHRONOUS_DEPENDENCY);
		final Executable<Void> webm_audio = ExecutorManager.globalAsyncSubmit(() -> {
			// WebM audio (Opus/Vorbis)
			if (!webmAudio.isEmpty()) {
				if (hasAudioLangs.get()) {
					final Map<String, List<YouTubeVideo>> trackedVids = ArrayUtils.splitListByFunctionWithHash(webmAudio, (e) -> {
						return e.getFormat().getTrackLanguage();
					});
					for (final Map.Entry<String, List<YouTubeVideo>> langs : trackedVids.entrySet()) {
						final int id = value.getAndIncrement();
						final E adaptationSet = period.add(lXML.e("AdaptationSet"));
						adaptationSet.add(new Attr("id", String.valueOf(id)));
						adaptationSet.add(new Attr("mimeType", "audio/webm"));
						if (ENABLE_SEGMENT_ALIGNMENT) {
							adaptationSet.add(new Attr("startWithSAP", "1"));
							adaptationSet.add(new Attr("segmentAlignment", "true"));
						} else if (ENABLE_SUB_SEGMENT_ALIGNMENT) {
							adaptationSet.add(new Attr("subsegmentStartsWithSAP", "1"));
							adaptationSet.add(new Attr("subsegmentAlignment", "true"));
						}
						adaptationSet.add(new Attr("lang", langs.getKey()));
						adaptationSet.add(new Attr("contentType", "audio"));
						for (final YouTubeVideo webm : langs.getValue()) {
							if (webm == null)
								continue;
							final E representation = adaptationSet.add(lXML.e("Representation"));
							representation.add(new Attr("id", String.valueOf(webm.getTag())));
							if (webm.getFormat().hasTrackLanguage())
								representation.add(new Attr("lang", webm.getFormat().getTrackID()));
							representation.add(new Attr("codecs", StringsUtil.substringBetweenO(webm.getCodecMime(), "\"", "\"")));
							representation.add(new Attr("bandwidth", String.valueOf(webm.getBitrate())));
							/* 1.5.3+ (Project Accessible) */
							if (webm.getFormat().hasTrackLanguage()) {
								final E label = representation.add(lXML.e("Label"));
								label.setText(webm.getFormat().getTrackLanguage());
							}
							final E role = representation.add(lXML.e("Role"));
							role.add(new Attr("schemeIdUri", "urn:mpeg:dash:role:2011"));
							role.add(new Attr("value", (webm.getFormat().isDefaultTrack() ? "main" : "alternate")));
							// init (0-xxx), index (xxx-xxxx)
							final E audioChannelConfiguration = representation.add(lXML.e("AudioChannelConfiguration"));
							audioChannelConfiguration.add(new Attr("schemeIdUri", "urn:mpeg:dash:23003:3:audio_channel_configuration:2011"));
							audioChannelConfiguration.add(new Attr("value", String.valueOf(webm.getFormat().getChannels())));
							representation.add(lXML.e("BaseURL", _intUrl + urlizer(webm.videoURL(enableCDNRandomization))));
							final E segmentBase = representation.add(lXML.e("SegmentBase"));
							segmentBase.add(new Attr("indexRange", webm.getIndexPayload()));
							final E initialization = segmentBase.add(lXML.e("Initialization"));
							initialization.add(new Attr("range", webm.getInitPayload()));
						}
					}
				} else {
					final int id = value.getAndIncrement();
					final E adaptationSet = period.add(lXML.e("AdaptationSet"));
					adaptationSet.add(new Attr("id", String.valueOf(id)));
					adaptationSet.add(new Attr("mimeType", "audio/webm"));
					if (ENABLE_SEGMENT_ALIGNMENT) {
						adaptationSet.add(new Attr("startWithSAP", "1"));
						adaptationSet.add(new Attr("segmentAlignment", "true"));
					} else if (ENABLE_SUB_SEGMENT_ALIGNMENT) {
						adaptationSet.add(new Attr("subsegmentStartsWithSAP", "1"));
						adaptationSet.add(new Attr("subsegmentAlignment", "true"));
					}
					adaptationSet.add(new Attr("contentType", "audio"));
					for (final YouTubeVideo webm : webmAudio) {
						if (webm == null)
							continue;
						final E representation = adaptationSet.add(lXML.e("Representation"));
						representation.add(new Attr("id", String.valueOf(webm.getTag())));
						if (webm.getFormat().hasTrackLanguage())
							representation.add(new Attr("lang", webm.getFormat().getTrackID()));
						representation.add(new Attr("codecs", StringsUtil.substringBetweenO(webm.getCodecMime(), "\"", "\"")));
						representation.add(new Attr("bandwidth", String.valueOf(webm.getBitrate())));
						/* 1.5.3+ (Project Accessible) */
						if (webm.getFormat().hasTrackLanguage()) {
							final E label = representation.add(lXML.e("Label"));
							label.setText(webm.getFormat().getTrackLanguage());
						}
						final E role = representation.add(lXML.e("Role"));
						role.add(new Attr("schemeIdUri", "urn:mpeg:dash:role:2011"));
						role.add(new Attr("value", (webm.getFormat().isDefaultTrack() ? "main" : "alternate")));
						// init (0-xxx), index (xxx-xxxx)
						final E audioChannelConfiguration = representation.add(lXML.e("AudioChannelConfiguration"));
						audioChannelConfiguration.add(new Attr("schemeIdUri", "urn:mpeg:dash:23003:3:audio_channel_configuration:2011"));
						audioChannelConfiguration.add(new Attr("value", String.valueOf(webm.getFormat().getChannels())));
						representation.add(lXML.e("BaseURL", _intUrl + urlizer(webm.videoURL(enableCDNRandomization))));
						final E segmentBase = representation.add(lXML.e("SegmentBase"));
						segmentBase.add(new Attr("indexRange", webm.getIndexPayload()));
						final E initialization = segmentBase.add(lXML.e("Initialization"));
						initialization.add(new Attr("range", webm.getInitPayload()));
					}
				}
			}
			log.debug("[DJS] generate::webm_audio");
			return null;
		}, IFutureTaskFlags.FT_HAS_SYNCHRONOUS_DEPENDENCY);
		final Executable<Void> mp4_av1_all = ExecutorManager.globalAsyncSubmit(() -> {
			if (!disableAV1codecs && mp4AV1 != null && !mp4AV1.isEmpty()) {
				final int id = value.getAndIncrement();
				final E adaptationSet = period.add(lXML.e("AdaptationSet"));
				adaptationSet.add(new Attr("id", String.valueOf(id)));
				adaptationSet.add(new Attr("mimeType", "video/mp4"));
				if (ENABLE_NO_CODING_DEPENDENCY) {
					adaptationSet.add(new Attr("codingDependency", "false"));
				}
				if (ENABLE_ADAPTATION_SET_SWITCHING) {
					adaptationSet.add(new Attr("bitstreamSwitching", "true"));
				}
				adaptationSet.add(new Attr("contentType", "video"));
				if (ENABLE_SEGMENT_ALIGNMENT) {
					adaptationSet.add(new Attr("startWithSAP", "1"));
					adaptationSet.add(new Attr("segmentAlignment", "true"));
				} else if (ENABLE_SUB_SEGMENT_ALIGNMENT) {
					adaptationSet.add(new Attr("subsegmentStartsWithSAP", "1"));
					adaptationSet.add(new Attr("subsegmentAlignment", "true"));
				}
				adaptationSet.add(new Attr("scanType", "progressive"));
				final List<Integer> widths = FastLists.list(mp4AV1.size());
				final List<Integer> heights = FastLists.list(mp4AV1.size());
				for (final YouTubeVideo webm : mp4AV1) {
					if (webm == null) {
						continue;
					} else if (!webm.validSegmentPayloads()) {
						log.warn("Invalid segment payloads: " + webm);
						continue;
					}

					final E representation = adaptationSet.add(lXML.e("Representation"));
					representation.add(new Attr("id", String.valueOf(webm.getTag())));
					representation.add(new Attr("codecs", StringsUtil.substringBetweenO(webm.getCodecMime(), "\"", "\"")));
					final String[] size = Splitters.splitString(webm.getSize(), "x");
					representation.add(new Attr("width", size[0]));
					representation.add(new Attr("height", size[1]));
					representation.add(new Attr("maxPlayoutRate", MAX_PLAYOUT_RATE_GLOBAL));
					representation.add(new Attr("bandwidth", String.valueOf(webm.getBitrate())));
					representation.add(new Attr("frameRate", String.valueOf(webm.getFPS())));
					representation.add(lXML.e("BaseURL", _intUrl + urlizer(webm.videoURL(enableCDNRandomization))));
					final E segmentBase = representation.add(lXML.e("SegmentBase"));
					segmentBase.add(new Attr("indexRange", webm.getIndexPayload()));
					segmentBase.add(lXML.e("Initialization").add(new Attr("range", webm.getInitPayload())));
					if (ENABLE_ADAPTATION_SET_DIMENSIONS) {
						widths.add(Integer.parseInt(size[0]));
						heights.add(Integer.parseInt(size[1]));
					}
				}
				if (ENABLE_ADAPTATION_SET_DIMENSIONS) {
					adaptationSet.add(new Attr("minWidth", String.valueOf(Collections.min(widths))));
					adaptationSet.add(new Attr("maxWidth", String.valueOf(Collections.max(widths))));
					adaptationSet.add(new Attr("minHeight", String.valueOf(Collections.min(heights))));
					adaptationSet.add(new Attr("maxHeight", String.valueOf(Collections.max(heights))));
				}
			}
			log.debug("[DJS] generate::mp4_av1_video");
			return null;
		}, IFutureTaskFlags.FT_HAS_SYNCHRONOUS_DEPENDENCY);
		final Executable<Void> mp4_h264_all = ExecutorManager.globalAsyncSubmit(() -> {
			// MP4 Video (-AV1)
			if (!mp4video.isEmpty()) {
				final int id = value.getAndIncrement();
				final E adaptationSet = period.add(lXML.e("AdaptationSet"));
				adaptationSet.add(new Attr("id", String.valueOf(id)));
				adaptationSet.add(new Attr("mimeType", "video/mp4"));
				if (ENABLE_NO_CODING_DEPENDENCY) {
					adaptationSet.add(new Attr("codingDependency", "false"));
				}
				if (ENABLE_ADAPTATION_SET_SWITCHING) {
					adaptationSet.add(new Attr("bitstreamSwitching", "true"));
				}
				adaptationSet.add(new Attr("contentType", "video"));
				if (ENABLE_SEGMENT_ALIGNMENT) {
					adaptationSet.add(new Attr("startWithSAP", "1"));
					adaptationSet.add(new Attr("segmentAlignment", "true"));
				} else if (ENABLE_SUB_SEGMENT_ALIGNMENT) {
					adaptationSet.add(new Attr("subsegmentStartsWithSAP", "1"));
					adaptationSet.add(new Attr("subsegmentAlignment", "true"));
				}
				adaptationSet.add(new Attr("scanType", "progressive"));
				final List<Integer> widths = FastLists.list(mp4video.size());
				final List<Integer> heights = FastLists.list(mp4video.size());
				for (final YouTubeVideo mp4 : mp4video) {
					if (mp4 == null)
						continue;
					else if (!mp4.validSegmentPayloads()) {
						log.warn("Invalid segment payloads: " + mp4);
						continue;
					}
					if ((djsRemove30FPSVideos && has60FPS.value() && (mp4.getFPS() == 24 || mp4.getFPS() == 30))
							&& (mp4.getFormat().getHeight() == 720 || mp4.getFormat().getHeight() == 1080 || mp4.getFormat().getHeight() == 1440
									|| mp4.getFormat().getHeight() == 2160 || mp4.getFormat().getHeight() == 4320)) {
						continue;
					}
					if ((djsRemove60FPSVideos && !djsRemove30FPSVideos && has60FPS.value() && (mp4.getFPS() > 30)) && (mp4.getFormat().getHeight() >= 720)) {
						continue;
					}
					final E representation = adaptationSet.add(lXML.e("Representation"));
					representation.add(new Attr("id", String.valueOf(mp4.getTag())));
					representation.add(new Attr("codecs", StringsUtil.substringBetweenO(mp4.getCodecMime(), "\"", "\"")));
					final String[] size = Splitters.splitString(mp4.getSize(), "x");
					representation.add(new Attr("width", size[0]));
					representation.add(new Attr("height", size[1]));
					representation.add(new Attr("maxPlayoutRate", MAX_PLAYOUT_RATE_GLOBAL));
					representation.add(new Attr("bandwidth", String.valueOf(mp4.getBitrate())));
					representation.add(new Attr("frameRate", String.valueOf(mp4.getFPS())));
					representation.add(lXML.e("BaseURL", _intUrl + urlizer(mp4.videoURL(enableCDNRandomization))));
					final E segmentBase = representation.add(lXML.e("SegmentBase"));
					segmentBase.add(new Attr("indexRange", mp4.getIndexPayload()));
					final E initialization = segmentBase.add(lXML.e("Initialization"));
					initialization.add(new Attr("range", mp4.getInitPayload()));
					if (ENABLE_ADAPTATION_SET_DIMENSIONS) {
						widths.add(Integer.parseInt(size[0]));
						heights.add(Integer.parseInt(size[1]));
					}
				}
				if (ENABLE_ADAPTATION_SET_DIMENSIONS) {
					adaptationSet.add(new Attr("minWidth", String.valueOf(Collections.min(widths))));
					adaptationSet.add(new Attr("maxWidth", String.valueOf(Collections.max(widths))));
					adaptationSet.add(new Attr("minHeight", String.valueOf(Collections.min(heights))));
					adaptationSet.add(new Attr("maxHeight", String.valueOf(Collections.max(heights))));
				}
			}
			log.debug("[DJS] generate::mp4_h264_video");
			return null;
		}, IFutureTaskFlags.FT_HAS_SYNCHRONOUS_DEPENDENCY);
		final Executable<Void> webm_vp9_all = ExecutorManager.globalAsyncSubmit(() -> {
			// WebM Video (-AV1)
			if (allowWebM && !webmVideo.isEmpty()) {
				final int id = value.getAndIncrement();
				final E adaptationSet = period.add(lXML.e("AdaptationSet"));
				adaptationSet.add(new Attr("id", String.valueOf(id)));
				adaptationSet.add(new Attr("mimeType", "video/webm"));
				if (ENABLE_NO_CODING_DEPENDENCY) {
					adaptationSet.add(new Attr("codingDependency", "false"));
				}
				if (ENABLE_ADAPTATION_SET_SWITCHING) {
					adaptationSet.add(new Attr("bitstreamSwitching", "true"));
				}
				adaptationSet.add(new Attr("contentType", "video"));
				if (ENABLE_SEGMENT_ALIGNMENT) {
					adaptationSet.add(new Attr("startWithSAP", "1"));
					adaptationSet.add(new Attr("segmentAlignment", "true"));
				} else if (ENABLE_SUB_SEGMENT_ALIGNMENT) {
					adaptationSet.add(new Attr("subsegmentStartsWithSAP", "1"));
					adaptationSet.add(new Attr("subsegmentAlignment", "true"));
				}
				adaptationSet.add(new Attr("scanType", "progressive"));
				final List<Integer> widths = FastLists.list(webmVideo.size());
				final List<Integer> heights = FastLists.list(webmVideo.size());
				for (final YouTubeVideo webm : webmVideo) {
					if (webm == null)
						continue;
					if (!webm.validSegmentPayloads()) {
						log.warn("Invalid segment payloads: " + webm);
						continue;
					}
					if (djsRemove30FPSVideos && has60FPS.value() && (webm.getFPS() == 24 || webm.getFPS() == 30)
							&& (webm.getFormat().getHeight() == 720 || webm.getFormat().getHeight() == 1080 || webm.getFormat().getHeight() == 1440
									|| webm.getFormat().getHeight() == 2160 || webm.getFormat().getHeight() == 4320)) {
						continue;
					}
					if ((djsRemove60FPSVideos && !djsRemove30FPSVideos && has60FPS.value() && (webm.getFPS() > 30)) && (webm.getFormat().getHeight() >= 720)) {
						continue;
					}
					final E representation = adaptationSet.add(lXML.e("Representation"));
					representation.add(new Attr("id", String.valueOf(webm.getTag())));
					representation.add(new Attr("codecs", StringsUtil.substringBetweenO(webm.getCodecMime(), "\"", "\"")));
					final String[] size = Splitters.splitString(webm.getSize(), "x");
					representation.add(new Attr("width", size[0]));
					representation.add(new Attr("height", size[1]));
					representation.add(new Attr("maxPlayoutRate", MAX_PLAYOUT_RATE_GLOBAL));
					representation.add(new Attr("bandwidth", String.valueOf(webm.getBitrate())));
					representation.add(new Attr("frameRate", String.valueOf(webm.getFPS())));
					representation.add(lXML.e("BaseURL", _intUrl + urlizer(webm.videoURL(enableCDNRandomization))));
					final E segmentBase = representation.add(lXML.e("SegmentBase"));
					segmentBase.add(new Attr("indexRange", webm.getIndexPayload()));
					final E initialization = segmentBase.add(lXML.e("Initialization"));
					initialization.add(new Attr("range", webm.getInitPayload()));
					if (ENABLE_ADAPTATION_SET_DIMENSIONS) {
						widths.add(Integer.parseInt(size[0]));
						heights.add(Integer.parseInt(size[1]));
					}
				}
				if (ENABLE_ADAPTATION_SET_DIMENSIONS) {
					adaptationSet.add(new Attr("minWidth", String.valueOf(Collections.min(widths))));
					adaptationSet.add(new Attr("maxWidth", String.valueOf(Collections.max(widths))));
					adaptationSet.add(new Attr("minHeight", String.valueOf(Collections.min(heights))));
					adaptationSet.add(new Attr("maxHeight", String.valueOf(Collections.max(heights))));
				}
			}
			log.debug("[DJS] generate::webm_vp9_video");
			return null;
		}, IFutureTaskFlags.FT_HAS_SYNCHRONOUS_DEPENDENCY);
		final Executable<Void> webm_av1_all = ExecutorManager.globalAsyncSubmit(() -> {
			if (allowWebM && !disableAV1codecs && webmAV1 != null && !webmAV1.isEmpty() && webmAV1.size() > 1) {
				final int id = value.getAndIncrement();
				final E adaptationSet = period.add(lXML.e("AdaptationSet"));
				adaptationSet.add(new Attr("id", String.valueOf(id)));
				adaptationSet.add(new Attr("mimeType", "video/webm"));
				if (ENABLE_NO_CODING_DEPENDENCY) {
					adaptationSet.add(new Attr("codingDependency", "false"));
				}
				if (ENABLE_ADAPTATION_SET_SWITCHING) {
					adaptationSet.add(new Attr("bitstreamSwitching", "true"));
				}
				adaptationSet.add(new Attr("contentType", "video"));
				if (ENABLE_SEGMENT_ALIGNMENT) {
					adaptationSet.add(new Attr("startWithSAP", "1"));
					adaptationSet.add(new Attr("segmentAlignment", "true"));
				} else if (ENABLE_SUB_SEGMENT_ALIGNMENT) {
					adaptationSet.add(new Attr("subsegmentStartsWithSAP", "1"));
					adaptationSet.add(new Attr("subsegmentAlignment", "true"));
				}
				adaptationSet.add(new Attr("scanType", "progressive"));
				final List<Integer> widths = FastLists.list(webmVideo.size());
				final List<Integer> heights = FastLists.list(webmVideo.size());
				for (final YouTubeVideo webm : webmAV1) {
					if (webm == null)
						continue;
					if (!webm.validSegmentPayloads()) {
						log.warn("Invalid segment payloads: " + webm);
						continue;
					}
					final E representation = adaptationSet.add(lXML.e("Representation"));
					representation.add(new Attr("id", String.valueOf(webm.getTag())));
					representation.add(new Attr("codecs", StringsUtil.substringBetweenO(webm.getCodecMime(), "\"", "\"")));
					final String[] size = Splitters.splitString(webm.getSize(), "x");
					representation.add(new Attr("width", size[0]));
					representation.add(new Attr("height", size[1]));
					representation.add(new Attr("maxPlayoutRate", MAX_PLAYOUT_RATE_GLOBAL));
					representation.add(new Attr("bandwidth", String.valueOf(webm.getBitrate())));
					representation.add(new Attr("frameRate", String.valueOf(webm.getFPS())));
					representation.add(lXML.e("BaseURL", _intUrl + urlizer(webm.videoURL(enableCDNRandomization))));
					final E segmentBase = representation.add(lXML.e("SegmentBase"));
					segmentBase.add(new Attr("indexRange", webm.getIndexPayload()));
					final E initialization = segmentBase.add(lXML.e("Initialization"));
					initialization.add(new Attr("range", webm.getInitPayload()));
					if (ENABLE_ADAPTATION_SET_DIMENSIONS) {
						widths.add(Integer.parseInt(size[0]));
						heights.add(Integer.parseInt(size[1]));
					}
				}
				if (ENABLE_ADAPTATION_SET_DIMENSIONS) {
					adaptationSet.add(new Attr("minWidth", String.valueOf(Collections.min(widths))));
					adaptationSet.add(new Attr("maxWidth", String.valueOf(Collections.max(widths))));
					adaptationSet.add(new Attr("minHeight", String.valueOf(Collections.min(heights))));
					adaptationSet.add(new Attr("maxHeight", String.valueOf(Collections.max(heights))));
				}
			}
			log.debug("[DJS] generate::webm_av1_video");
			return null;
		}, IFutureTaskFlags.FT_HAS_SYNCHRONOUS_DEPENDENCY);
		if (log.isTraceEnabled()) {
			log.trace("await[MPD_GEN]:");
			final Executable<?>[] awaits = new Executable<?>[]
				{ mp4_audio, webm_audio, mp4_av1_all, mp4_h264_all, webm_vp9_all, webm_av1_all };
			for (final Executable<?> o : awaits)
				log.trace("\t" + o);
		}

		final List<Executable<?>> o = ExecutorManager.get().awaitFor(5, TimeUnit.SECONDS, mp4_audio, webm_audio, mp4_av1_all, mp4_h264_all, webm_vp9_all,
				webm_av1_all);
		if (!o.isEmpty()) {
			log.error("Failed to fully wait on XML generation!");
			log.error("Failed by: ");
			TaskUtils.printWithTrace(o);
		}
		ExecutorManager.globalAsyncSubmit(() -> {
			mp4AV1.clear();
			webmAV1.clear();
			mp4video.clear();
			mpeg4audio.clear();
			webmAudio.clear();
			webmVideo.clear();
			log.debug("[DJS] Cleared up GC.");
			return null;
		});
		if (ENABLE_ADAPTATION_SET_SWITCHING) {
			final List<Node> adaptationSet = period.children();
			if (adaptationSet != null) {
				final List<Integer> videoSets = FastLists.list(3);
				/* First iteration, get available video sets. */
				for (final Node _set : adaptationSet) {
					if (!(_set instanceof E))
						continue;
					final E set = (E) _set;
					if (!set.getAttr("contentType").value().equals("video")) {
						continue;
					}
					videoSets.add(set.getInt("id", -1));
				}
				/* Second iteration, append the tag. */
				for (final Node _set : adaptationSet) {
					if (!(_set instanceof E))
						continue;
					final E set = (E) _set;
					final int id = set.getInt("id", -1);
					if (id == -1) {
						continue;
					}
					if (!videoSets.contains(id)) {
						continue;
					}
					final E attr = set.add(lXML.e("SupplementalProperty"));
					attr.add(new Attr("schemeIdUri", "urn:mpeg:dash:adaptation-set-switching:2016"));
					final Integer[] _val = videoSets.stream().filter(val -> val != id).toArray(Integer[]::new);
					final String val = ArrayUtils.join(_val, ",");
					attr.add(new Attr("value", val));
				}
			} else {
				log.warn("Failed to generate switching sets! [adaptionSets == NULL]");
			}
		}
		return d.toString();
	}

}
